home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2005 October / PCWOCT05.iso / Software / FromTheMag / XAMPP 1.4.14 / xampp-win32-1.4.14-installer.exe / xampp / php / pear / XML / Parser.php < prev    next >
PHP Script  |  2004-10-01  |  19KB  |  671 lines

  1. <?php
  2. //
  3. // +----------------------------------------------------------------------+
  4. // | PHP Version 4                                                        |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1997-2004 The PHP Group                                |
  7. // +----------------------------------------------------------------------+
  8. // | This source file is subject to version 3.0 of the PHP license,       |
  9. // | that is bundled with this package in the file LICENSE, and is        |
  10. // | available at through the world-wide-web at                           |
  11. // | http://www.php.net/license/3_0.txt.                                  |
  12. // | If you did not receive a copy of the PHP license and are unable to   |
  13. // | obtain it through the world-wide-web, please send a note to          |
  14. // | license@php.net so we can mail you a copy immediately.               |
  15. // +----------------------------------------------------------------------+
  16. // | Author: Stig Bakken <ssb@fast.no>                                    |
  17. // |         Tomas V.V.Cox <cox@idecnet.com>                              |
  18. // |         Stephan Schmidt <schst@php-tools.net>                        |
  19. // +----------------------------------------------------------------------+
  20. //
  21. // $Id: Parser.php,v 1.14 2004/05/25 13:26:42 schst Exp $
  22.  
  23. /**
  24.  * XML Parser class.
  25.  *
  26.  * This is an XML parser based on PHP's "xml" extension,
  27.  * based on the bundled expat library.
  28.  *
  29.  * @category XML
  30.  * @package XML_Parser
  31.  * @author  Stig Bakken <ssb@fast.no>
  32.  * @author  Tomas V.V.Cox <cox@idecnet.com>
  33.  * @author  Stephan Schmidt <schst@php-tools.net>
  34.  */
  35.  
  36. /**
  37.  * uses PEAR's error handling
  38.  */
  39. require_once 'PEAR.php';
  40.  
  41. /**
  42.  * resource could not be created
  43.  */
  44. define('XML_PARSER_ERROR_NO_RESOURCE', 200);
  45.  
  46. /**
  47.  * unsupported mode
  48.  */
  49. define('XML_PARSER_ERROR_UNSUPPORTED_MODE', 201);
  50.  
  51. /**
  52.  * invalid encoding was given
  53.  */
  54. define('XML_PARSER_ERROR_INVALID_ENCODING', 202);
  55.  
  56. /**
  57.  * specified file could not be read
  58.  */
  59. define('XML_PARSER_ERROR_FILE_NOT_READABLE', 203);
  60.  
  61. /**
  62.  * invalid input
  63.  */
  64. define('XML_PARSER_ERROR_INVALID_INPUT', 204);
  65.  
  66. /**
  67.  * remote file cannot be retrieved in safe mode
  68.  */
  69. define('XML_PARSER_ERROR_REMOTE', 205);
  70.  
  71. /**
  72.  * XML Parser class.
  73.  *
  74.  * This is an XML parser based on PHP's "xml" extension,
  75.  * based on the bundled expat library.
  76.  *
  77.  * Notes:
  78.  * - It requires PHP 4.0.4pl1 or greater
  79.  * - From revision 1.17, the function names used by the 'func' mode
  80.  *   are in the format "xmltag_$elem", for example: use "xmltag_name"
  81.  *   to handle the <name></name> tags of your xml file.
  82.  *
  83.  * @category XML
  84.  * @package XML_Parser
  85.  * @author  Stig Bakken <ssb@fast.no>
  86.  * @author  Tomas V.V.Cox <cox@idecnet.com>
  87.  * @author  Stephan Schmidt <schst@php-tools.net>
  88.  * @todo    create XML_Parser_Namespace to parse documents with namespaces
  89.  * @todo    create XML_Parser_Pull
  90.  * @todo    Tests that need to be made:
  91.  *          - mixing character encodings
  92.  *          - a test using all expat handlers
  93.  *          - options (folding, output charset)
  94.  *          - different parsing modes
  95.  */
  96. class XML_Parser extends PEAR
  97. {
  98.     // {{{ properties
  99.  
  100.    /**
  101.      * XML parser handle
  102.      *
  103.      * @var  resource
  104.      * @see  xml_parser_create()
  105.      */
  106.     var $parser;
  107.  
  108.     /**
  109.      * File handle if parsing from a file
  110.      *
  111.      * @var  resource
  112.      */
  113.     var $fp;
  114.  
  115.     /**
  116.      * Whether to do case folding
  117.      *
  118.      * If set to true, all tag and attribute names will
  119.      * be converted to UPPER CASE.
  120.      *
  121.      * @var  boolean
  122.      */
  123.     var $folding = true;
  124.  
  125.     /**
  126.      * Mode of operation, one of "event" or "func"
  127.      *
  128.      * @var  string
  129.      */
  130.     var $mode;
  131.  
  132.     /**
  133.      * Mapping from expat handler function to class method.
  134.      *
  135.      * @var  array
  136.      */
  137.     var $handler = array(
  138.         'character_data_handler'            => 'cdataHandler',
  139.         'default_handler'                   => 'defaultHandler',
  140.         'processing_instruction_handler'    => 'piHandler',
  141.         'unparsed_entity_decl_handler'      => 'unparsedHandler',
  142.         'notation_decl_handler'             => 'notationHandler',
  143.         'external_entity_ref_handler'       => 'entityrefHandler'
  144.     );
  145.  
  146.     /**
  147.      * source encoding
  148.      *
  149.      * @var string
  150.      */
  151.     var $srcenc;
  152.  
  153.     /**
  154.      * target encoding
  155.      *
  156.      * @var string
  157.      */
  158.     var $tgtenc;
  159.  
  160.     /**
  161.      * handler object
  162.      *
  163.      * @var object
  164.      */
  165.     var $_handlerObj;
  166.  
  167.     // }}}
  168.     // {{{ constructor
  169.  
  170.     /**
  171.      * Creates an XML parser.
  172.      *
  173.      * This is needed for PHP4 compatibility, it will
  174.      * call the constructor, when a new instance is created.
  175.      *
  176.      * @param string $srcenc source charset encoding, use NULL (default) to use
  177.      *                       whatever the document specifies
  178.      * @param string $mode   how this parser object should work, "event" for
  179.      *                       startelement/endelement-type events, "func"
  180.      *                       to have it call functions named after elements
  181.      * @param string $tgenc  a valid target encoding
  182.      */
  183.     function XML_Parser($srcenc = null, $mode = 'event', $tgtenc = null)
  184.     {
  185.         XML_Parser::__construct($srcenc, $mode, $tgtenc);
  186.     }
  187.     // }}}
  188.  
  189.     /**
  190.      * PHP5 constructor
  191.      *
  192.      * @param string $srcenc source charset encoding, use NULL (default) to use
  193.      *                       whatever the document specifies
  194.      * @param string $mode   how this parser object should work, "event" for
  195.      *                       startelement/endelement-type events, "func"
  196.      *                       to have it call functions named after elements
  197.      * @param string $tgenc  a valid target encoding
  198.      */
  199.     function __construct($srcenc = null, $mode = 'event', $tgtenc = null)
  200.     {
  201.         $this->PEAR('XML_Parser_Error');
  202.  
  203.         $this->mode   = $mode;
  204.         $this->srcenc = $srcenc;
  205.         $this->tgtenc = $tgtenc;
  206.     }
  207.     // }}}
  208.  
  209.     /**
  210.      * Sets the mode of the parser.
  211.      *
  212.      * Possible modes are:
  213.      * - func
  214.      * - event
  215.      *
  216.      * You can set the mode using the second parameter
  217.      * in the constructor.
  218.      *
  219.      * This method is only needed, when switching to a new
  220.      * mode at a later point.
  221.      *
  222.      * @access  public
  223.      * @param   string          mode, either 'func' or 'event'
  224.      * @return  boolean|object  true on success, PEAR_Error otherwise   
  225.      */
  226.     function setMode($mode)
  227.     {
  228.         if ($mode != 'func' && $mode != 'event') {
  229.             $this->raiseError('Unsupported mode given', XML_PARSER_ERROR_UNSUPPORTED_MODE);
  230.         }
  231.  
  232.         $this->mode = $mode;
  233.         return true;
  234.     }
  235.  
  236.     /**
  237.      * Sets the object, that will handle the XML events
  238.      *
  239.      * This allows you to create a handler object independent of the
  240.      * parser object that you are using and easily switch the underlying
  241.      * parser.
  242.      *
  243.      * If no object will be set, XML_Parser assumes that you
  244.      * extend this class and handle the events in $this.
  245.      *
  246.      * @access  public
  247.      * @param   object      object to handle the events
  248.      * @return  boolean     will always return true
  249.      * @since   v1.2.0beta3
  250.      */
  251.     function setHandlerObj(&$obj)
  252.     {
  253.         $this->_handlerObj = &$obj;
  254.         return true;
  255.     }
  256.  
  257.     /**
  258.      * Init the element handlers
  259.      *
  260.      * @access  private
  261.      */
  262.     function _initHandlers()
  263.     {
  264.         if (!is_resource($this->parser)) {
  265.             return false;
  266.         }
  267.  
  268.         if (!is_object($this->_handlerObj)) {
  269.             $this->_handlerObj = &$this;
  270.         }
  271.         switch ($this->mode) {
  272.  
  273.             case 'func':
  274.                 xml_set_object($this->parser, $this);
  275.                 xml_set_element_handler($this->parser, 'funcStartHandler', 'funcEndHandler');
  276.                 break;
  277.  
  278.             case 'event':
  279.                 xml_set_object($this->parser, $this->_handlerObj);
  280.                 xml_set_element_handler($this->parser, 'startHandler', 'endHandler');
  281.                 break;
  282.             default:
  283.                 return $this->raiseError('Unsupported mode given', XML_PARSER_ERROR_UNSUPPORTED_MODE);
  284.                 break;
  285.         }
  286.  
  287.  
  288.         /**
  289.          * set additional handlers for character data, entities, etc.
  290.          */
  291.         foreach ($this->handler as $xml_func => $method) {
  292.             if (method_exists($this->_handlerObj, $method)) {
  293.                 $xml_func = 'xml_set_' . $xml_func;
  294.                 $xml_func($this->parser, $method);
  295.             }
  296.         }
  297.     }
  298.  
  299.     // {{{ _create()
  300.  
  301.     /**
  302.      * create the XML parser resource
  303.      *
  304.      * Has been moved from the constructor to avoid
  305.      * problems with object references.
  306.      *
  307.      * Furthermore it allows us returning an error
  308.      * if something fails.
  309.      *
  310.      * @access   private
  311.      * @return   boolean|object     true on success, PEAR_Error otherwise
  312.      *
  313.      * @see xml_parser_create
  314.      */
  315.     function _create()
  316.     {
  317.         if ($this->srcenc === null) {
  318.             $xp = @xml_parser_create();
  319.         } else {
  320.             $xp = @xml_parser_create($this->srcenc);
  321.         }
  322.         if (is_resource($xp)) {
  323.             if ($this->tgtenc !== null) {
  324.                 if (!@xml_parser_set_option($xp, XML_OPTION_TARGET_ENCODING,
  325.                                             $this->tgtenc)) {
  326.                     return $this->raiseError('invalid target encoding', XML_PARSER_ERROR_INVALID_ENCODING);
  327.                 }
  328.             }
  329.             $this->parser = $xp;
  330.             $result = $this->_initHandlers($this->mode);
  331.             if ($this->isError($result)) {
  332.                 return $result;
  333.             }
  334.             xml_parser_set_option($xp, XML_OPTION_CASE_FOLDING, $this->folding);
  335.  
  336.             return true;
  337.         }
  338.         return $this->raiseError('Unable to create XML parser resource.', XML_PARSER_ERROR_NO_RESOURCE);
  339.     }
  340.  
  341.     // }}}
  342.     // {{{ reset()
  343.  
  344.     /**
  345.      * Reset the parser.
  346.      *
  347.      * This allows you to use one parser instance
  348.      * to parse multiple XML documents.
  349.      *
  350.      * @access   public
  351.      * @return   boolean|object     true on success, PEAR_Error otherwise
  352.      */
  353.     function reset()
  354.     {
  355.         $result = $this->_create();
  356.         if ($this->isError( $result )) {
  357.             return $result;
  358.         }
  359.     }
  360.  
  361.     // }}}
  362.     // {{{ setInputFile()
  363.  
  364.     /**
  365.      * Sets the input xml file to be parsed
  366.      *
  367.      * @param    string      Filename (full path)
  368.      * @return   resource    fopen handle of the given file
  369.      * @throws   XML_Parser_Error
  370.      * @see      setInput(), setInputString(), parse()
  371.      * @access   public
  372.      */
  373.     function setInputFile($file)
  374.     {
  375.         /**
  376.          * check, if file is a remote file
  377.          */
  378.         if (eregi('^(http|ftp)://', substr($file, 0, 10))) {
  379.             if (!ini_get('safe_mode')) {
  380.                 ini_set('allow_url_fopen', 1);
  381.             } else {
  382.                 return $this->raiseError('Remote files cannot be parsed, as safe mode is enabled.', XML_PARSER_ERROR_REMOTE);
  383.             }
  384.         }
  385.         
  386.         $fp = @fopen($file, 'rb');
  387.         if (is_resource($fp)) {
  388.             $this->fp = $fp;
  389.             return $fp;
  390.         }
  391.         return $this->raiseError('File could not be opened.', XML_PARSER_ERROR_FILE_NOT_READABLE);
  392.     }
  393.  
  394.     // }}}
  395.     // {{{ setInputString()
  396.     
  397.     /**
  398.      * XML_Parser::setInputString()
  399.      * 
  400.      * Sets the xml input from a string
  401.      * 
  402.      * @param string $data a string containing the XML document
  403.      * @return null
  404.      **/
  405.     function setInputString($data)
  406.     {
  407.         $this->fp = $data;
  408.         return null;
  409.     }
  410.     
  411.     // }}}
  412.     // {{{ setInput()
  413.  
  414.     /**
  415.      * Sets the file handle to use with parse().
  416.      *
  417.      * You should use setInputFile() or setInputString() if you
  418.      * pass a string 
  419.      *
  420.      * @param    mixed  $fp  Can be either a resource returned from fopen(),
  421.      *                       a URL, a local filename or a string.
  422.      * @access   public
  423.      * @see      parse()
  424.      * @uses     setInputString(), setInputFile()
  425.      */
  426.     function setInput($fp)
  427.     {
  428.         if (is_resource($fp)) {
  429.             $this->fp = $fp;
  430.             return true;
  431.         }
  432.         // see if it's an absolute URL (has a scheme at the beginning)
  433.         elseif (eregi('^[a-z]+://', substr($fp, 0, 10))) {
  434.             return $this->setInputFile($fp);
  435.         }
  436.         // see if it's a local file
  437.         elseif (file_exists($fp)) {
  438.             return $this->setInputFile($fp);
  439.         }
  440.         // it must be a string
  441.         else {
  442.             $this->fp = $fp;
  443.             return true;
  444.         }
  445.  
  446.         return $this->raiseError('Illegal input format', XML_PARSER_ERROR_INVALID_INPUT);
  447.     }
  448.  
  449.     // }}}
  450.     // {{{ parse()
  451.  
  452.     /**
  453.      * Central parsing function.
  454.      *
  455.      * @return   true|object PEAR error     returns true on success, or a PEAR_Error otherwise
  456.      * @access   public
  457.      */
  458.     function parse()
  459.     {
  460.         /**
  461.          * reset the parser
  462.          */
  463.         $result = $this->reset();
  464.         if ($this->isError($result)) {
  465.             return $result;
  466.         }
  467.         // if $this->fp was fopened previously
  468.         if (is_resource($this->fp)) {
  469.         
  470.             while ($data = fread($this->fp, 4096)) {
  471.                 if (!$this->_parseString($data, feof($this->fp))) {
  472.                     return $this->raiseError();
  473.                 }
  474.             }
  475.         // otherwise, $this->fp must be a string
  476.         } else {
  477.             if (!$this->_parseString($this->fp, true)) {
  478.                 return $this->raiseError();
  479.             }
  480.         }
  481.         $this->free();
  482.  
  483.         return true;
  484.     }
  485.  
  486.     /**
  487.      * XML_Parser::_parseString()
  488.      * 
  489.      * @param string $data
  490.      * @param boolean $eof
  491.      * @return bool
  492.      * @access private
  493.      * @see parseString()
  494.      **/
  495.     function _parseString($data, $eof = false)
  496.     {
  497.         return xml_parse($this->parser, $data, $eof);
  498.     }
  499.     
  500.     // }}}
  501.     // {{{ parseString()
  502.  
  503.     /**
  504.      * XML_Parser::parseString()
  505.      * 
  506.      * Parses a string.
  507.      *
  508.      * @param    string  $data XML data
  509.      * @param    boolean $eof  If set and TRUE, data is the last piece of data sent in this parser
  510.      * @throws   XML_Parser_Error
  511.      * @return   Pear Error|true   true on success or a PEAR Error
  512.      * @see      _parseString()
  513.      */
  514.     function parseString($data, $eof = false)
  515.     {
  516.         if (!isset($this->parser) || !is_resource($this->parser)) {
  517.             $this->reset();
  518.         }
  519.         
  520.         if (!$this->_parseString($data, $eof)) {
  521.            return $this->raiseError();
  522.         }
  523.  
  524.         if ($eof === true) {
  525.             $this->free();
  526.         }
  527.         return true;
  528.     }
  529.     
  530.     /**
  531.      * XML_Parser::free()
  532.      * 
  533.      * Free the internal resources associated with the parser
  534.      * 
  535.      * @return null
  536.      **/
  537.     function free()
  538.     {
  539.         if (is_resource($this->parser)) {
  540.             xml_parser_free($this->parser);
  541.             unset( $this->parser );
  542.         }
  543.         if (isset($this->fp) && is_resource($this->fp)) {
  544.             fclose($this->fp);
  545.         }
  546.         unset($this->fp);
  547.         return null;
  548.     }
  549.     
  550.     /**
  551.      * XML_Parser::raiseError()
  552.      * 
  553.      * Trows a XML_Parser_Error and free's the internal resources
  554.      * 
  555.      * @param string  $msg   the error message
  556.      * @param integer $ecode the error message code
  557.      * @return XML_Parser_Error 
  558.      **/
  559.     function raiseError($msg = null, $ecode = 0)
  560.     {
  561.         $msg = !is_null($msg) ? $msg : $this->parser;
  562.         $err = &new XML_Parser_Error($msg, $ecode);
  563.         $this->free();
  564.         return parent::raiseError($err);
  565.     }
  566.     
  567.     // }}}
  568.     // {{{ funcStartHandler()
  569.  
  570.     function funcStartHandler($xp, $elem, $attribs)
  571.     {
  572.         $func = 'xmltag_' . $elem;
  573.         if (method_exists($this->_handlerObj, $func)) {
  574.             call_user_func(array(&$this->_handlerObj, $func), $xp, $elem, $attribs);
  575.         }
  576.     }
  577.  
  578.     // }}}
  579.     // {{{ funcEndHandler()
  580.  
  581.     function funcEndHandler($xp, $elem)
  582.     {
  583.         $func = 'xmltag_' . $elem . '_';
  584.         if (method_exists($this, $func)) {
  585.             call_user_func(array(&$this->_handlerObj, $func), $xp, $elem);
  586.         }
  587.     }
  588.  
  589.     // }}}
  590.     // {{{ startHandler()
  591.  
  592.     /**
  593.      *
  594.      * @abstract
  595.      */
  596.     function startHandler($xp, $elem, &$attribs)
  597.     {
  598.         return NULL;
  599.     }
  600.  
  601.     // }}}
  602.     // {{{ endHandler()
  603.  
  604.     /**
  605.      *
  606.      * @abstract
  607.      */
  608.     function endHandler($xp, $elem)
  609.     {
  610.         return NULL;
  611.     }
  612.  
  613.  
  614.     // }}}
  615. }
  616.  
  617. /**
  618.  * error class, replaces PEAR_Error
  619.  *
  620.  * An instance of this class will be returned
  621.  * if an error occurs inside XML_Parser.
  622.  *
  623.  * There are three advantages over using the standard PEAR_Error:
  624.  * - All messages will be prefixed
  625.  * - check for XML_Parser error, using is_a( $error, 'XML_Parser_Error' )
  626.  * - messages can be generated from the xml_parser resource
  627.  *
  628.  * @package XML_Parser
  629.  * @access  public
  630.  * @see     PEAR_Error
  631.  */
  632. class XML_Parser_Error extends PEAR_Error
  633. {
  634.     // {{{ properties
  635.  
  636.    /**
  637.     * prefix for all messages
  638.     *
  639.     * @var      string
  640.     */    
  641.     var $error_message_prefix = 'XML_Parser: ';
  642.  
  643.     // }}}
  644.     // {{{ constructor()
  645.    /**
  646.     * construct a new error instance
  647.     *
  648.     * You may either pass a message or an xml_parser resource as first
  649.     * parameter. If a resource has been passed, the last error that
  650.     * happened will be retrieved and returned.
  651.     *
  652.     * @access   public
  653.     * @param    string|resource     message or parser resource
  654.     * @param    integer             error code
  655.     * @param    integer             error handling
  656.     * @param    integer             error level
  657.     */    
  658.     function XML_Parser_Error($msgorparser = 'unknown error', $code = 0, $mode = PEAR_ERROR_RETURN, $level = E_USER_NOTICE)
  659.     {
  660.         if (is_resource($msgorparser)) {
  661.             $code = xml_get_error_code($msgorparser);
  662.             $msgorparser = sprintf('%s at XML input line %d',
  663.                                    xml_error_string($code),
  664.                                    xml_get_current_line_number($msgorparser));
  665.         }
  666.         $this->PEAR_Error($msgorparser, $code, $mode, $level);
  667.     }
  668.     // }}}
  669. }
  670. ?>
  671.